#include "IAP.h"
#include "delay.h"
#include "flash.h"
#include "USART_Configuration.h"
#include <stdbool.h>
#include "AT32Models.h"
#include "at32f4xx.h"
uint16_t iap_fail_flag = 0;
// u32 download_full = 0;
// u8 download_progress = 0;
// u8 download_laststep = 0;
// uint8_t batterylevel_cnt = 0;
u32 IAP_Program_Flash_Addr = 0;
u32 IAP_Program_Flash_TotalSize = 0;
u32 IAP_Program_Flash_CNT = 0;
u32 IAP_Read_Flash_Addr = 0;
u32 IAP_Read_Flash_TotalSize = 0;
// u32 IAP_Read_Flash_CNT = 0;
u32 IAP_TIMEOUT_CNT = 0;
IAP_STATUS_Typedef IAP_STATUS = IDLE;
bool IAP_SYNC_Require_FLAG = 0;
bool IAP_SYNC = 0;
bool IAP_RX_Version_R_FLAG = 0;
// u8 IAP_GET_CMD_FLAG = 0;
// u8 IAP_GET_DATA_FLAG = 0;
u8 IAP_Wrong_CMD = 0;
// uint8_t batter_logoLevel = 0;
// uint32_t battery_logo_cnt = 0;
IAP_RX_SEQUENCE_STATUS_TYPE IAP_Rx_Step;
IAP_RX_SEQUENCE_TYPE IAP_RX_Sequence;
// u16 usart1_rx_cnt = 0;
// u8 usart1_rx_flag = 0;
// #define IAP_DEBUG

char IAP_XOR_Check()
{
	uint8_t xor = 0;
	for (uint16_t i = 0; i < IAP_RX_Sequence.SIZE; i++) // xor calculta
	{
		xor ^= IAP_RX_Sequence.Buff[i];
	}
	if (xor == IAP_RX_Sequence.XOR)
	{

#ifdef IAP_DEBUG
		usart_sendBytes("\nXOR check.\n");
#endif // IAP_DEBUG
		return 1;
	}
	else
	{
#ifdef IAP_DEBUG
		// usart_sendBytes("\n XOR err, expect: %x\n", xor);
		usart_sendBytes("\n XOR err\n");
#endif // IAP_DEBUG
		return 0;
	}
}
void IAP_STATUS_CMD_Mode_Handle()
{
	// delay_ms(10);
	switch (IAP_RX_Sequence.Buff[1])
	{
	case IAP_GetVersion_CMD:
		if (IAP_RX_Sequence.Buff[2] == (IAP_GetVersion_CMD ^ 0xFF))
		{
			usart_sendByte(IAP_ACK);
			usart_sendByte(0x02);
			usart_sendByte(Version_ID1);
			usart_sendByte(Version_ID2);
		}
#ifdef IAP_DEBUG
		usart_sendBytes("received Getversion cmd\n");
#endif // IAP_DEBUG
		break;
	case IAP_GetID_CMD:
		if (IAP_RX_Sequence.Buff[2] == (IAP_GetID_CMD ^ 0xFF))
		{
			usart_sendByte(IAP_ACK);
			usart_sendByte(0x04);
			usart_sendByte(whoAmI.Serial);
			usart_sendByte(whoAmI.ID >> 16);
			usart_sendByte(whoAmI.ID >> 8);
			usart_sendByte(whoAmI.ID);
		}
#ifdef IAP_DEBUG
		usart_sendBytes("received GetID cmd\n");
#endif // IAP_DEBUG
		break;
	case IAP_Write_CMD:
		if (IAP_RX_Sequence.Buff[2] == (IAP_Write_CMD ^ 0xFF))
		{
			usart_sendByte(IAP_ACK);
			IAP_STATUS = Data_Mode_RX;
		}
#ifdef IAP_DEBUG
		usart_sendBytes("\nreceived write cmd\n");
		usart_sendBytes("\nenter write_data mode\n");
#endif // IAP_DEBUG
		break;
	case IAP_Read_CMD:
		if (IAP_RX_Sequence.Buff[2] == (IAP_Read_CMD ^ 0xFF))
		{
			usart_sendByte(IAP_ACK);
			IAP_STATUS = Data_Mode_TX;
		}
#ifdef IAP_DEBUG
		usart_sendBytes("\nreceived read cmd\n");
		usart_sendBytes("\nenter read_data mode\n");
#endif // IAP_DEBUG
		break;
	case IAP_JUMP_CMD:
		if (IAP_RX_Sequence.Buff[2] == (IAP_JUMP_CMD ^ 0xFF))
		{
			usart_sendByte(IAP_ACK);
			IAP_STATUS = IAP_JUMP;
		}
		break;
	default:
		usart_sendByte(IAP_NACK);
#ifdef IAP_DEBUG
		usart_sendBytes("cmd err\n");
#endif // IAP_DEBUG
		break;
	}
}
void IAP_STATUS_Data_Mode_RX_Handle()
{
	IAP_Program_Flash_Addr = ((u32)IAP_RX_Sequence.Buff[1] << 24) + ((u32)IAP_RX_Sequence.Buff[2] << 16) + ((u32)IAP_RX_Sequence.Buff[3] << 8) + (u32)IAP_RX_Sequence.Buff[4];
	IAP_Program_Flash_TotalSize = ((u32)IAP_RX_Sequence.Buff[5] << 24) + ((u32)IAP_RX_Sequence.Buff[6] << 16) + ((u32)IAP_RX_Sequence.Buff[7] << 8) + (u32)IAP_RX_Sequence.Buff[8];

	if ((IAP_Program_Flash_Addr >= APP_START_ADDR) && (IAP_Program_Flash_Addr <= (0x08000000 + APP_FLASH_SIZE)))
	{
		uint16_t pageSize = IAP_Program_Flash_TotalSize / Flash_PageSize;
		if (pageSize * Flash_PageSize < IAP_Program_Flash_TotalSize)
		{
			pageSize += 1;
		}

		usart_sendByte(IAP_ACK);
		// pageSize = 5;
		usart_sendByte((pageSize >> 8));
		usart_sendByte(pageSize);
		// usart_sendByte(0x11);
		// usart_sendByte(0x22);
		// usart_sendByte(0x33);
		// usart_sendByte(0x44);

		FLASH_Unlock();
		for (uint16_t page = 0; page < pageSize; page++)
		{

			usart_sendByte(page >> 8);
			usart_sendByte(page);
			FLASH_ErasePage(APP_START_ADDR + page * Flash_PageSize);
			while (FLASH_GetBank1Status() != FLASH_PRC_DONE)
				;
			IAP_TIMEOUT_CNT = systick_cnt_ms; // reset timeout counter
		}
		usart_sendByte(IAP_ACK);
		// Flash_AppSector_Erase();
		IAP_STATUS = Data_Receiving;
		// download_full = IAP_Program_Flash_TotalSize;
		// RGB_ClearSrceen();
		// downloadArcDisp();

#ifdef IAP_DEBUG
		// usart_sendBytes("\nreceived addr: %lx TotaSize: %ld\n", IAP_Program_Flash_Addr, IAP_Program_Flash_TotalSize);
		usart_sendBytes("\n Enter data receiving mode\n");
#endif // IAP_DEBUG
	}
	else
	{
		usart_sendByte(IAP_NACK);
#ifdef IAP_DEBUG
		// usart_sendBytes("\nreceived addr: %lx TotaSize: %ld\n", IAP_Program_Flash_Addr, IAP_Program_Flash_TotalSize);
		usart_sendBytes("write addr no legal\n");
#endif // IAP_DEBUG
	}
}
void IAP_STATUS_Data_Mode_TX_Handle()
{
	IAP_Read_Flash_Addr = ((u32)IAP_RX_Sequence.Buff[1] << 24) + ((u32)IAP_RX_Sequence.Buff[2] << 16) + ((u32)IAP_RX_Sequence.Buff[3] << 8) + (u32)IAP_RX_Sequence.Buff[4];
	IAP_Read_Flash_TotalSize = ((u32)IAP_RX_Sequence.Buff[5] << 24) + ((u32)IAP_RX_Sequence.Buff[6] << 16) + ((u32)IAP_RX_Sequence.Buff[7] << 8) + (u32)IAP_RX_Sequence.Buff[8];
	if ((IAP_Read_Flash_Addr >= APP_START_ADDR) && (IAP_Program_Flash_Addr <= (0x08000000 + 65536)))
	{
		usart_sendByte(IAP_ACK);
		IAP_STATUS = Data_Sending;
#ifdef IAP_DEBUG
		// usart_sendBytes("\nreceived addr: 0x%lx TotalSize: %ld\n", IAP_Read_Flash_Addr, IAP_Read_Flash_TotalSize);
		usart_sendBytes("\nEnter data sending mode\n");
#endif // IAP_DEBUG
		IAP_STATUS_Data_Send_Handle();
	}
	else
	{
		usart_sendByte(IAP_NACK);
#ifdef IAP_DEBUG
		// usart_sendBytes("\nreceived addr: %lx TotalSize: %ld\n", IAP_Read_Flash_Addr, IAP_Read_Flash_TotalSize);
		usart_sendBytes("\nread addr no legal");
#endif // IAP_DEBUG
	}
}

// IAP_Load_APP�������Ż�
//#pragma GCC push_options
//#pragma GCC optimize('O0')
//  void MSR_MSP(u32 addr)
//  {
//  	__ASM volatile("MSR MSP, r0"); 			//set Main Stack value
//  	__ASM volatile("BX r14");
//  }
void app_clear_sys_status()
{
	/*Close Peripherals Clock*/

	// RCC->APB2RST = 0XFFFF;
	// RCC->APB2RST = 0;
	// RCC->APB1RST = 0XFFFF;
	// RCC->APB1RST = 0;
	// RCC->AHBEN = 0;
	// RCC->APB2EN = 0;
	// RCC->APB1EN = 0;
	// RCC->APB1RST = 0xFFFFFFFF;
	// 	RCC->APB2RST = 0xFFFFFFFF;
	// 	RCC->APB1RST = 0x00000000;
	// 	RCC->APB2RST = 0x00000000;
	// RCC->BDC = 0;
	// RCC->CTRLSTS = 0;
	// RCC->CTRL = 0;
	RCC->CFG = 0;
	// RCC->CLKINT = 0;
#if defined(AT32F415xx)
	/*Close PLL*/
	/* Reset SW, AHBDIV, APB1DIV, APB2DIV, ADCDIV and CLKOUT_SEL bits */
	// RCC->AHBRST = 0XFFFF;
	// RCC->AHBRST = 0;
	// RCC->PLL = 0;
// #else
	// RCC->RESERVED[2] = 0;
#endif
	// RCC->MISC = 0;
	/* Disable all interrupts and clear pending bits */
	// __set_PRIMASK(1);
// #if defined(AT32F413xx)
// 	RCC->RESERVED1[7] = 0;
// 	RCC->TEST = 0;
// 	RCC->MISC2 = 0;
// 	RCC->RESERVED2 = 0;
	// RCC->INTCTRL = 0;
// #elif defined(AT32F415xx)
// 	RCC->RESERVED1[8] = 0;
// 	RCC->MISC2 = 0;
// #endif
// 	/*Colse Systick*/
	// SysTick->CTRL = 0;
	// SysTick->LOAD = 0;
	// SysTick->VAL = 0;
	// RCC->APB2RST = 0XFFFF;
	// RCC->APB2RST = 0;
	// RCC->APB1RST = 0XFFFF;
	// RCC->APB1RST = 0;
	// RCC->AHBEN = 0;
	// RCC->APB2EN = 0;
	// RCC->APB1EN = 0;
	// // RCC->PLL=0;
	// for (uint8_t i = 0; i < 8; i++)
	// {
	// 	NVIC->ICER[i]=0xFFFFFFFF;
	// 	NVIC->ICPR[i]=0xFFFFFFFF;
	// }
	// __set_PRIMASK(0);
}
volatile iapfun jump2app;
static void IAP_Load_APP(u32 appxaddr)
{
	// if (((*(vu32 *)appxaddr) & 0x2FFE0000) == 0x20000000) //检查栈顶地址是否合法.
	{
		jump2app = (iapfun) * (vu32 *)(appxaddr + 4); //用户代码区第二个字为程序开始地址(复位地址)
		__set_MSP(*(__IO uint32_t*) appxaddr);
		// MSR_MSP(*(vu32*)appxaddr);							//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app(); //跳转到APP.
	}
}
//#pragma GCC pop_options
void IAP_STATUS_JUMP_Handle(void)
{
	uint32_t IAP_JumpAddr = ((u32)IAP_RX_Sequence.Buff[1] << 24) + ((u32)IAP_RX_Sequence.Buff[2] << 16) + ((u32)IAP_RX_Sequence.Buff[3] << 8) + (u32)IAP_RX_Sequence.Buff[4];
	usart_sendByte(IAP_ACK);
	app_clear_sys_status();
	IAP_Load_APP(IAP_JumpAddr);
}
void IAP_STATUS_Data_Receive_Handle()
{
	if (IAP_Program_Flash_TotalSize <= IAP_MAX_Packet_Size)
	{
		Flash_write_16bits(IAP_Program_Flash_Addr, &IAP_RX_Sequence.Buff[1], (IAP_RX_Sequence.SIZE - 1));
		IAP_Program_Flash_CNT += IAP_RX_Sequence.SIZE - 1;
#ifdef IAP_DEBUG
		usart_sendBytes("\nIAP_STATUS_Data_Receive_Handle\n");
		// usart_sendBytes("\nTotal received: %ld Byte, received done.\n", IAP_Program_Flash_CNT);
		usart_sendBytes("\nProgram done \n");
		usart_sendBytes("\nEnter CMD mode\n");
#endif // IAP_DEBUG
		IAP_Program_Flash_CNT = 0;
		usart_sendByte(IAP_ACK);
		IAP_STATUS = CMD_Mode;
		FLASH_Lock();
	}
	else if (IAP_Program_Flash_TotalSize > IAP_MAX_Packet_Size)
	{

		/*Singal_RGB_XY(2, 2, 0, 0, 100);*/
		Flash_write_16bits(IAP_Program_Flash_Addr, &IAP_RX_Sequence.Buff[1], (IAP_RX_Sequence.SIZE - 1));
		IAP_Program_Flash_Addr += IAP_RX_Sequence.SIZE - 1;
		IAP_Program_Flash_CNT += IAP_RX_Sequence.SIZE - 1;
		usart_sendByte(IAP_ACK);
		// download_progress = IAP_Program_Flash_CNT * 16 / download_full;

		if (IAP_Program_Flash_CNT >= IAP_Program_Flash_TotalSize)
		{
			/*Singal_RGB_XY(2, 2, 0, 100, 100);*/

			IAP_Program_Flash_CNT = 0;
#ifdef IAP_DEBUG
			// usart_sendBytes("\nTotal received: %ld Byte, received done.\n", IAP_Program_Flash_CNT);
			usart_sendBytes("\nEnter CMD mode\n");
#endif // IAP_DEBUG
	   // dowmploadComplete();
			usart_sendByte(IAP_ACK);
			IAP_STATUS = CMD_Mode;
			iap_fail_flag = IAP_Download_Success;
			FLASH_ProgramHalfWord(IAP_Download_Status_ADDR, iap_fail_flag);
			FLASH_Lock();
			IAP_SYNC = 0;
		}
	}
}
void IAP_STATUS_Data_Send_Handle()
{
	/*Singal_RGB_XY(3, 3, 100, 0, 0);*/
	u32 raddr = IAP_Read_Flash_Addr;
	u32 i = 0;
#ifdef IAP_DEBUG
	usart_sendBytes("\nRead: ");
#endif // IAP_DEBUG
	FLASH_Unlock();
	for (i = 0; i < IAP_Read_Flash_TotalSize; i++)
	{
		uint8_t temp = *(__IO uint8_t *)raddr;
		usart_sendByte(temp);
#ifdef IAP_DEBUG
		// usart_sendBytes("0x%x ", temp);
#endif // IAP_DEBUG
		IAP_TIMEOUT_CNT = systick_cnt_ms;
		raddr++;
	}
	FLASH_Lock();
#ifdef IAP_DEBUG
	// usart_sendBytes("Total read: %d bytes\n", i);
	usart_sendBytes("\nSending done\n");
	usart_sendBytes("\nEnter CMD mode\n");
#endif // IAP_DEBUG

	IAP_STATUS = CMD_Mode;
}
// void IAP_STATUS_TIMEOUT_Handle()
//{
//	//IAP_Load_APP(APP_START_ADDR);
//#ifdef IAP_DEBUG
//	usart_sendBytes("\nTime out\n");
//#endif // IAP_DEBUG
// }

void IAP_Handle()
{

	/*usart_sendBytes("\systick_cnt_ms %ld\n", systick_cnt_ms);*/
	if (systick_cnt_ms > (IAP_TIMEOUT_CNT + IAP_TIMEOUT)) // timeout
	{
#ifdef IAP_DEBUG
		// usart_sendBytes("Total read: %d bytes\n", i);
		char str[10];
		usart_sendBytes("\nIAP_TIMEOUT_CNT\t");
		usart_sendBytes(myitoa(systick_cnt_ms, str, 10));
#endif // IAP_DEBUG
		/*usart_sendBytes("Read df: 0x%.4X\n", IAP_Download_Flag);*/
		if (IAP_Download_Flag == IAP_Download_Fail)
		{
			// RGB_ClearSrceen();
			// downloadArcDisp();
			// donwloadClearFial();
			// downloadFail();
			while (1)
				;
		}
		if (IAP_STATUS == Data_Receiving)
		{
			if (IAP_Program_Flash_CNT < IAP_Program_Flash_TotalSize)
			{
				iap_fail_flag = IAP_Download_Fail;
				FLASH_ProgramHalfWord(IAP_Download_Status_ADDR, iap_fail_flag);
				// donwloadClearFial();
				// downloadFail();
				/*while (1);*/
			}
		}
		if (IAP_Download_Flag != IAP_Download_Success)
		{
			// donwloadClearFial();
			// downloadFail();
			// while (1);
		}
		/*IAP_STATUS = TIME_OUT;*/
		/*usart_sendBytes("\nIAP_Load_APP %ld\n", systick_cnt_ms);*/
		// RCC->APB1RST = 0xFFFFFFFF;
		// RCC->APB2RST = 0xFFFFFFFF;
		// RCC->APB1RST = 0x00000000;
		// RCC->APB2RST = 0x00000000;
		IAP_Load_APP(APP_START_ADDR);
	}
	if (!IAP_SYNC)
	{
/*Singal_RGB(16, 200, 0, 0);*/
// if ((systick_cnt_ms - battery_logo_cnt) > 100)
// {
// batter_logoLevel = (BatterVoltage - 3.3) / 0.075;
// BateryLevel_disp(batterylevel_cnt);
// if (batterylevel_cnt < batter_logoLevel)
// {
// 	batterylevel_cnt++;
// }
// battery_logo_cnt = systick_cnt_ms;
// batter_logoLevel++;
// }
#ifdef IAP_DEBUG
		// usart_sendBytes("\nno sync\n");
#endif // IAP_DEBUG
		if (IAP_SYNC_Require_FLAG)
		{
			usart_sendByte(IAP_ACK);
			IAP_SYNC_Require_FLAG = 0;
			IAP_SYNC = 1;
			IAP_STATUS = CMD_Mode;
			/*Singal_RGB(16, 0, 0, 200);*/
#ifdef IAP_DEBUG
			usart_sendBytes("\nIAP sync\n Enter command mode\n");
#endif // IAP_DEBUG
		}
	}
	if (IAP_Wrong_CMD)
	{
		usart_sendByte(IAP_NACK);
		IAP_Wrong_CMD = 0;
	}
	if (IAP_Rx_Step == DONE)
	{
#ifdef IAP_DEBUG
		u16 i;
		// usart_sendBytes("Data  Size:%d\n", IAP_RX_Sequence.SIZE);
		usart_sendBytes("DataBuff:");
		for (i = 1; i < IAP_RX_Sequence.SIZE; i++)
		{
			// usart_sendBytes("0x%x ", IAP_RX_Sequence.Buff[i]);
		}
		// usart_sendBytes("\nXor: 0x%x\n", IAP_RX_Sequence.XOR);
#endif // IAP_DEBUG
		if (IAP_XOR_Check())
		{
			switch (IAP_STATUS)
			{
			case CMD_Mode:
				IAP_STATUS_CMD_Mode_Handle();
				break; // CMD
			case Data_Mode_RX:
				IAP_STATUS_Data_Mode_RX_Handle();
				break; // RX
			case Data_Mode_TX:
				IAP_STATUS_Data_Mode_TX_Handle();
				break; // TX
			case Data_Receiving:
				IAP_STATUS_Data_Receive_Handle();
				break; // RX_ING
			case IAP_JUMP:
				IAP_STATUS_JUMP_Handle();
				break;
			// case Data_Sending: IAP_STATUS_Data_Send_Handle(); break;//����Ҫ����������
			// case TIME_OUT: IAP_STATUS_TIMEOUT_Handle(); break;//100ms��û��������ʱ����
			default:
				break;
			}
		}
		else
		{
			usart_sendByte(IAP_NACK);
		}

		IAP_Rx_Step = BEGIN;
	}
}
void IAP_USART_Handle(u8 Rx_Data)
{
	IAP_TIMEOUT_CNT = systick_cnt_ms; // reset timeout counter

	if (IAP_SYNC) // connection established
	{
		if (IAP_Rx_Step == BEGIN)
		{
			IAP_RX_Sequence.SIZE = Rx_Data;
			IAP_RX_Sequence.Buff_cnt = 0;
			IAP_Rx_Step = RECEIVING;
		}
		IAP_RX_Sequence.Buff[IAP_RX_Sequence.Buff_cnt++] = Rx_Data;
		if (IAP_RX_Sequence.Buff_cnt > IAP_RX_Sequence.SIZE)
		{
			IAP_RX_Sequence.XOR = Rx_Data;
			IAP_Rx_Step = DONE;
		}
	}
	else // connection not established
	{
		if (Rx_Data == IAP_SYNC_CMD)
		{
			IAP_SYNC_Require_FLAG = 1;
			IAP_Rx_Step = BEGIN;
		}
		else
		{

			IAP_Wrong_CMD = 1;
		}
	}
}